﻿using System;
using System.Collections.Generic;
using System.Linq;
#if NETFX_CORE
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

#else
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Threading;
#endif
// The Templated Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234235

namespace StyleMVVM.View
{
	public static class VisualStates
	{
		public static string GroupBusyStatus
		{
			get { return "BusyStatusStates"; }
		}

		public static string StateBusy
		{
			get { return "Busy"; }
		}

		public static string StateIdle
		{
			get { return "Idle"; }
		}

		public static string GroupVisibility
		{
			get { return "VisibilityStates"; }
		}

		public static string StateVisible
		{
			get { return "Visible"; }
		}

		public static string StateHidden
		{
			get { return "Hidden"; }
		}
	}

	/// <summary>
	/// A control to provide a visual indicator when an application is busy.
	/// </summary>
	/// <QualityBand>Preview</QualityBand>
	[TemplateVisualState(Name = "Idle", GroupName = "BusyStatusStates")]
	[TemplateVisualState(Name = "Busy", GroupName = "BusyStatusStates")]
	[TemplateVisualState(Name = "Visible", GroupName = "VisibilityStates")]
	[TemplateVisualState(Name = "Hidden", GroupName = "VisibilityStates")]
	[StyleTypedProperty(Property = "OverlayStyle", StyleTargetType = typeof(Rectangle))]
	[StyleTypedProperty(Property = "ProgressBarStyle", StyleTargetType = typeof(ProgressBar))]
	public sealed class BusyIndicator : ContentControl
	{
		/// <summary>
		/// Gets or sets a value indicating whether the BusyContent is visible.
		/// </summary>
		protected bool IsContentVisible { get; set; }

		/// <summary>
		/// Timer used to delay the initial display and avoid flickering.
		/// </summary>
		private readonly DispatcherTimer _displayAfterTimer;

		/// <summary>
		/// Instantiates a new instance of the BusyIndicator control.
		/// </summary>
		public BusyIndicator()
		{
			DefaultStyleKey = typeof(BusyIndicator);
			_displayAfterTimer = new DispatcherTimer();
			Loaded += delegate { _displayAfterTimer.Tick += (DisplayAfterTimerElapsed); };
			Unloaded += delegate
				            {
					            _displayAfterTimer.Tick -= (DisplayAfterTimerElapsed);
					            _displayAfterTimer.Stop();
				            };
		}

		/// <summary>
		/// Overrides the OnApplyTemplate method.
		/// </summary>
#if NETFX_CORE
		protected override void OnApplyTemplate()
#else
		public override void OnApplyTemplate()
#endif
		{
			base.OnApplyTemplate();
			ChangeVisualState(false);
		}

		/// <summary>
		/// Handler for the DisplayAfterTimer.
		/// </summary>
		/// <param name="sender">Event sender.</param>
		/// <param name="e">Event arguments.</param>
		private void DisplayAfterTimerElapsed(object sender, object e)
		{
			_displayAfterTimer.Stop();
			IsContentVisible = true;
			ChangeVisualState(true);
		}

		/// <summary>
		/// Changes the control's visual state(s).
		/// </summary>
		/// <param name="useTransitions">True if state transitions should be used.</param>
		private void ChangeVisualState(bool useTransitions)
		{
			VisualStateManager.GoToState(this, IsBusy ? VisualStates.StateBusy : VisualStates.StateIdle, useTransitions);
			VisualStateManager.GoToState(this,
			                             IsContentVisible ? VisualStates.StateVisible : VisualStates.StateHidden,
			                             useTransitions);
		}

		/// <summary>
		/// Gets or sets a value indicating whether the busy indicator should show.
		/// </summary>
		public bool IsBusy
		{
			get { return (bool)GetValue(InternalIsBusyProperty); }
			set { SetValue(InternalIsBusyProperty, value); }
		}

		public static DependencyProperty IsBusyProperty
		{
			get { return InternalIsBusyProperty; }
		}

		/// <summary>
		/// Identifies the IsBusy dependency property.
		/// </summary>
		internal static readonly DependencyProperty InternalIsBusyProperty = DependencyProperty.Register(
			"IsBusy",
			typeof(bool),
			typeof(BusyIndicator),
			new PropertyMetadata(false, new PropertyChangedCallback(OnIsBusyChanged)));

		/// <summary>
		/// IsBusyProperty property changed handler.
		/// </summary>
		/// <param name="d">BusyIndicator that changed its IsBusy.</param>
		/// <param name="e">Event arguments.</param>
		private static void OnIsBusyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BusyIndicator)d).OnIsBusyChanged(e);
		}

		/// <summary>
		/// IsBusyProperty property changed handler.
		/// </summary>
		/// <param name="e">Event arguments.</param>
		private void OnIsBusyChanged(DependencyPropertyChangedEventArgs e)
		{
			if (IsBusy)
			{
				if (DisplayAfter.Equals(TimeSpan.Zero))
				{
					// Go visible now
					IsContentVisible = true;
				}
				else
				{
					// Set a timer to go visible
					_displayAfterTimer.Interval = DisplayAfter;
					_displayAfterTimer.Start();
				}
			}
			else
			{
				// No longer visible
				_displayAfterTimer.Stop();
				IsContentVisible = false;
			}
			ChangeVisualState(true);
		}

		/// <summary>
		/// Gets or sets a value indicating the busy content to display to the user.
		/// </summary>
		public object BusyContent
		{
			get { return (object)GetValue(InternalBusyContentProperty); }
			set { SetValue(InternalBusyContentProperty, value); }
		}

		public static DependencyProperty BusyContentProperty
		{
			get { return InternalBusyContentProperty; }
		}

		/// <summary>
		/// Identifies the BusyContent dependency property.
		/// </summary>
		internal static readonly DependencyProperty InternalBusyContentProperty = DependencyProperty.Register(
			"BusyContent",
			typeof(object),
			typeof(BusyIndicator),
			new PropertyMetadata(null));

		/// <summary>
		/// Gets or sets a value indicating the template to use for displaying the busy content to the user.
		/// </summary>
		public DataTemplate BusyContentTemplate
		{
			get { return (DataTemplate)GetValue(InternalBusyContentTemplateProperty); }
			set { SetValue(InternalBusyContentTemplateProperty, value); }
		}

		public static DependencyProperty BusyContentTemplateProperty
		{
			get { return InternalBusyContentTemplateProperty; }
		}

		/// <summary>
		/// Identifies the BusyTemplate dependency property.
		/// </summary>
		internal static readonly DependencyProperty InternalBusyContentTemplateProperty = DependencyProperty.Register(
			"BusyContentTemplate",
			typeof(DataTemplate),
			typeof(BusyIndicator),
			new PropertyMetadata(null));

		/// <summary>
		/// Gets or sets a value indicating how long to delay before displaying the busy content.
		/// </summary>
		public TimeSpan DisplayAfter
		{
			get { return (TimeSpan)GetValue(InternalDisplayAfterProperty); }
			set { SetValue(InternalDisplayAfterProperty, value); }
		}

		public static DependencyProperty DisplayAfterProperty
		{
			get { return InternalDisplayAfterProperty; }
		}

		/// <summary>
		/// Identifies the DisplayAfter dependency property.
		/// </summary>
		internal static readonly DependencyProperty InternalDisplayAfterProperty = DependencyProperty.Register(
			"DisplayAfter",
			typeof(TimeSpan),
			typeof(BusyIndicator),
			new PropertyMetadata(TimeSpan.FromSeconds(0.1)));

		/// <summary>
		/// Gets or sets a value indicating the style to use for the overlay.
		/// </summary>
		public Style OverlayStyle
		{
			get { return (Style)GetValue(InternalOverlayStyleProperty); }
			set { SetValue(InternalOverlayStyleProperty, value); }
		}

		public static DependencyProperty OverlayStyleProperty
		{
			get { return InternalOverlayStyleProperty; }
		}

		/// <summary>
		/// Identifies the OverlayStyle dependency property.
		/// </summary>
		internal static readonly DependencyProperty InternalOverlayStyleProperty = DependencyProperty.Register(
			"OverlayStyle",
			typeof(Style),
			typeof(BusyIndicator),
			new PropertyMetadata(null));

		/// <summary>
		/// Gets or sets a value indicating the style to use for the progress bar.
		/// </summary>
		public Style ProgressBarStyle
		{
			get { return (Style)GetValue(InternalProgressBarStyleProperty); }
			set { SetValue(InternalProgressBarStyleProperty, value); }
		}

		public static DependencyProperty ProgressBarStyleProperty
		{
			get { return InternalProgressBarStyleProperty; }
		}

		/// <summary>
		/// Identifies the ProgressBarStyle dependency property.
		/// </summary>
		internal static readonly DependencyProperty InternalProgressBarStyleProperty = DependencyProperty.Register(
			"ProgressBarStyle",
			typeof(Style),
			typeof(BusyIndicator),
			new PropertyMetadata(null));
	}
}